package cn.jetpiece.cloud.common.app.modular.websocket.manager;

import cn.jetpiece.cloud.jwt.utils.JwtTokenUtil;
import cn.jetpiece.cloud.redis.contants.RedisMessageConstant;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 接受WebSocket消息
 *
 * @author XuJZ
 */
@Slf4j
@Component
@ServerEndpoint("/common/websocket/{tenantId}/{username}/{token}")
public class WebSocket {

    /**
     * 业务处理器beanName传递参数
     */
    public static final String HANDLER_NAME = "socketMessageHandler";

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    private static JwtTokenUtil jwtTokenUtil;

    private Session session;

    private String username;

    private Long tenantId;

    /**
     * 缓存 webSocket连接到单机服务class中（整体方案支持集群）
     */
    private static final CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();

    @OnOpen
    public void onOpen(Session session, @PathParam(value = "tenantId") Long tenantId, @PathParam(value = "username") String username, @PathParam(value = "token") String token) {
        try {
            if (!jwtTokenUtil().checkToken(token)) {
                session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "token认失败"));
                return;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "token认失败"));
            } catch (IOException ex) {
                log.error(ex.getMessage(), ex);
            }
            return;
        }
        try {
            this.session = session;
            this.tenantId = tenantId;
            this.username = username;
            webSockets.add(this);
            log.info("【websocket消息】有新的连接，总数为:" + webSockets.size());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @OnClose
    public void onClose(Session session) {
        try {
            for (WebSocket websocket : webSockets) {
                if (websocket.session.getId().equals(session.getId())) {
                    webSockets.remove(websocket);
                    break;
                }
            }
            log.info("【websocket消息】连接断开，总数为:" + webSockets.size());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 服务端推送消息
     *
     * @param tenantId 租户ID
     * @param username 用户账号
     * @param message  消息
     */
    public void pushMessage(Long tenantId, String username, String message) {
        try {
            for (WebSocket websocket : webSockets) {
                if (websocket.tenantId.equals(tenantId) && websocket.username.equals(username)) {
                    log.info("【websocket消息】 单点消息:" + message);
                    websocket.session.getBasicRemote().sendText(message);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 服务端推送消息
     *
     * @param tenantId 租户ID
     * @param message  消息
     */
    public void pushMessage(Long tenantId, String message) {
        try {
            for (WebSocket websocket : webSockets) {
                if (websocket.tenantId.equals(tenantId)) {
                    log.info("【websocket消息】 单点消息:" + message);
                    websocket.session.getBasicRemote().sendText(message);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 服务器端推送消息
     *
     * @param message 消息
     */
    public void pushMessage(String message) {
        try {
            webSockets.forEach(ws -> ws.session.getAsyncRemote().sendText(message));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @OnMessage
    public void onMessage(String message) {
        //todo 现在有个定时任务刷，应该去掉
        log.debug("【websocket消息】收到客户端消息:" + message);
//        JSONObject obj = new JSONObject();
        //业务类型
//        obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_CHECK);
        //消息内容
//        obj.put(WebsocketConst.MSG_TXT, "心跳响应");
        for (WebSocket webSocket : webSockets) {
            webSocket.pushMessage(message);
        }
    }

    /**
     * 广播消息
     * 后台发送消息到redis
     *
     * @param message 消息
     */
    public void sendMessage(JSONObject message) {
        JSONObject obj = new JSONObject();
        obj.put("message", message.toJSONString());
        send(obj);
    }

    /**
     * 此为单点消息
     *
     * @param username 用户账号
     * @param message  消息
     */
    public void sendMessage(String username, JSONObject message) {
        JSONObject obj = new JSONObject();
        obj.put("username", username);
        obj.put("message", message.toJSONString());
        send(obj);
    }

    /**
     * 此为单点消息
     *
     * @param tenantId 租户编号
     * @param username 用户账号
     * @param message  消息
     */
    public void sendMessage(Long tenantId, String username, JSONObject message) {
        JSONObject obj = new JSONObject();
        obj.put("tenantId", tenantId);
        obj.put("username", username);
        obj.put("message", message.toJSONString());
        send(obj);
    }

    private void send(JSONObject obj) {
        obj.put(RedisMessageConstant.HANDLER_NAME, HANDLER_NAME);
        redisTemplate.convertAndSend(RedisMessageConstant.REDIS_TOPIC_NAME, obj);
    }

    public JwtTokenUtil jwtTokenUtil() {
        if (jwtTokenUtil == null) {
            jwtTokenUtil = JwtTokenUtil.getINSTANCE();
        }
        return jwtTokenUtil;
    }
}
